import { Injectable } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
import * as jwt from "jsonwebtoken";

type LoginStatus = "no token" | "wrong token" | "not exists user" | "verify error" | "login";
export interface LoginInformation {
  status: LoginStatus;
  userID?: number;
  username?: string;
}

@Injectable()
export class UserTokenService {
  constructor(
    private readonly prisma: PrismaClient,
  ) {}

  generateToken(data: {
    userID: number;
    username: string;
    password: string;
  }): string {
    const token = jwt.sign(
      {
        id: data.userID,
        username: data.username,
      },
      data.password,
      { expiresIn: 36000 },
    );
    return token;
  }

  async isLogin(request: any): Promise<LoginInformation> {
    function createInfo(status: LoginStatus, userID?: number, username?: string) {
      return {
        status,
        userID,
        username,
      }
    }
    const token = this.getTokenFromRequest(request);
    if (!token) {
      return createInfo("no token");
    }
    const userData = this.getUserDataFromToken(token);
    if (!userData || !userData.id || !userData.username) {
      return createInfo("wrong token");
    }

    const user = await this.prisma.user.findUnique({
      select: {
        id: true,
        password: true,
      },
      where: {
        id: userData.id,
        username: userData.username,
      },
    });
    if (!user) {
      return createInfo("not exists user");
    }

    try {
      await this.verifyToken(token, user.password);
      return createInfo("login", userData.id, userData.username);
    } catch (e) {
      console.error(e);
      return createInfo("verify error");
    }
  }
  verifyToken(token: string, password: string): Promise<void> {
    return new Promise((resolve, reject) => {
      jwt.verify(token, password, (err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(undefined);
      });
    });
  }

  getTokenFromRequest(request: any): string | null {
    return request.headers["token"] || request.headers["Token"] || null;
  }

  getUserIDFromToken(token: string): number | null {
    const decodeToken = jwt.decode(token);
    if (!decodeToken) {
      return null;
    }
    return decodeToken["id"];
  }
  getUserIDFromRequest(request: any): number | null {
    const token = this.getTokenFromRequest(request);
    if (!token) {
      return null;
    }
    return this.getUserIDFromToken(token);
  }

  getUsernameFromToken(token: string): string | null {
    const decodeToken = jwt.decode(token);
    if (!decodeToken) {
      return null;
    }
    return decodeToken["username"];
  }
  getUsernameFromRequest(request: any): string | null {
    const token = this.getTokenFromRequest(request);
    if (!token) {
      return null;
    }
    return this.getUsernameFromToken(token);
  }

  getUserDataFromToken(token: string): { id: number; username: string } | null {
    const decodeToken = jwt.decode(token);
    if (!decodeToken) {
      return null;
    }
    return {
      id: decodeToken["id"],
      username: decodeToken["username"],
    };
  }
  getUserDataFromRequest(request: any): { id: number; username: string } | null {
    const token = this.getTokenFromRequest(request);
    return this.getUserDataFromToken(token);
  }
}
